package link.jfire.simplerpc.server.messagehandler;

import java.lang.reflect.InvocationTargetException;
import java.nio.charset.Charset;
import java.util.Map;
import javax.annotation.Resource;
import link.jfire.baseutil.collection.ByteCache;
import link.jfire.baseutil.simplelog.ConsoleLogFactory;
import link.jfire.baseutil.simplelog.Logger;
import link.jfire.fose.Fose;
import link.jfire.fose.util.IOUtil;
import link.jfire.simplerpc.exception.NoSuchMethodException;
import link.jfire.simplerpc.exception.NoSuchProxyException;
import link.jfire.socket.socketserver.bus.Message;
import link.jfire.socket.socketserver.handler.MessageHandler;
import sun.reflect.MethodAccessor;

@SuppressWarnings("restriction")
@Resource
public class InvokeMessageHandler implements MessageHandler
{
    private int                           limitLength;
    private String[]                      proxyNames;
    private Object[]                      impls;
    private Map<String, MethodAccessor>[] methodMaps;
    private ThreadLocal<Fose>             threadLocalLbse = new ThreadLocal<Fose>() {
        protected Fose initialValue()
        {
            return new Fose();
        }
    };
    private static Charset                charset         = Charset.forName("utf8");
    private Logger                        logger          = ConsoleLogFactory.getLogger();
    
    @Override
    public byte interestedDataPacketType()
    {
        return InvokeType.METHOD_INVOKE;
    }
    
    private String getString(ByteCache cache)
    {
        int length = IOUtil.readInt(cache);
        return cache.toString(charset, length);
    }
    
    private Object invoke(String proxyName, String methodName, Object[] args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException
    {
        for (int i = 0; i < limitLength; i++)
        {
            if (proxyNames[i].equals(proxyName))
            {
                MethodAccessor method = methodMaps[i].get(methodName);
                if (method == null)
                {
                    throw new NoSuchMethodException(methodName);
                }
                logger.debug("正确解析rpc数据，进行方法调用");
                return method.invoke(impls[i], args);
            }
        }
        throw new NoSuchProxyException(proxyName);
        
    }
    
    /**
     * 设置必须的工作单元，包含代理名称数组，代理实例，以及各实例方法名和方法访问映射数组
     * 
     * @param proxyNames
     * @param impls
     * @param methodMaps
     */
    public void setWorkUnit(String[] proxyNames, Object[] impls, Map<String, MethodAccessor>[] methodMaps)
    {
        this.proxyNames = proxyNames;
        this.impls = impls;
        this.methodMaps = methodMaps;
        limitLength = proxyNames.length;
    }
    
    @Override
    public void handler(Message message)
    {
        String proxyName = null;
        String methodName = null;
        int argsNum = -1;
        Object[] args;
        try
        {
            ByteCache businessData = message.getBusinessData();
            proxyName = getString(businessData);
            methodName = getString(businessData);
            argsNum = IOUtil.readInt(businessData);
            args = new Object[argsNum];
            Fose lbse = threadLocalLbse.get();
            for (int i = 0; i < argsNum; i++)
            {
                args[i] = lbse.deserialize(businessData);
            }
            Object result = invoke(proxyName, methodName, args);
            ByteCache reply = lbse.serialize(result);
            businessData.clear().putByteCache(reply);
        }
        catch (Exception e)
        {
            logger.error("远程调用出现失败,代理名称是{}，方法名称是{}，方法参数个数是{}", proxyName, methodName, argsNum, e);
            Fose lbse = threadLocalLbse.get();
            message.requestFail();
            message.getBusinessData().clear().putByteCache(lbse.serialize(e));
        }
    }
}
